#include "Texture2D.h"
#include "Timer.h"
#include "Game.h"

#include "IL/ilut.h"
#include "IL/il.h"
#include "IL/ilu.h"

#include <GL/gl.h>
#include <GL/glu.h>


/***************************************************
// Class XY_Coordinate
***************************************************/

XY_Coordinate::XY_Coordinate(float _x, float _y, float _u, float _v) {
	x = _x; 
	y = _y; 
	u = _u; 
	v = _v;
}

XY_Coordinate::~XY_Coordinate() {
}

/***************************************************
// Class Animation
***************************************************/

Animation::Animation(void) {
	frame_delay = 0;
	current_frame = 0;
	does_loop = true;
}

Animation::~Animation(void) {
}

void Animation::NextFrame()
{
	int num_frames = coordinates.size();

	current_frame++;
	if(current_frame >= num_frames)
	{
		if(does_loop)	// If it loops, sets the frame to the first 
			current_frame = 0;
		else			// else it sets it to the last frame
			current_frame = num_frames - 1;
	}
}

/***************************************************
// Class Texture2D
***************************************************/

/* Constructor / Deconstructor */
Texture2D::Texture2D(void) {
	number_of_animations = 0;
	current_animation = 0;
}

Texture2D::Texture2D( std::string texture_file_name, 
					  int num_of_animations, int current_anim,
					  int frame_size_x, int frame_size_y)
{
	this->LoadTexture(texture_file_name.c_str());
	
	if (num_of_animations == 0) {
		SetNumberOfAnimations(1);
		current_animation = 0;
		SetFrameSize(sheet.width, sheet.height);
		AddAnimationFrame(0, sheet.width, sheet.height);
	}
	else {
		SetNumberOfAnimations(num_of_animations);
		current_animation = current_anim;
		SetFrameSize(frame_size_x, frame_size_y);
		AddAnimationFrame(0, frame_size_x, frame_size_y);
	}
}

Texture2D::~Texture2D(void) {
}

/* Load / Initialize Functions */
void Texture2D::LoadTexture(const char *file_name) 
{	
	g_error = glGetError();

	ILuint image_ID;
	GLuint texture_ID;

	ILboolean success;
	ILenum error;

	ilGenImages(1, &image_ID);

	ilBindImage(image_ID);

	g_error = glGetError();

	success = ilLoadImage(file_name);

	if (success) {
		
		// Flipping the image the right side up (if it's flipped)
		ILinfo image_information;
		iluGetImageInfo(&image_information);
		if (image_information.Origin == IL_ORIGIN_UPPER_LEFT) {
			iluFlipImage();
		}

		// Converting the image
		success = ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);

		// Quitting if the conversion fails
		if (!success) {
			error = ilGetError();
			exit(-1);
		}

		// Generate a new texture
		glGenTextures(1, &texture_ID);
 
		// Bind the texture to a name
		glBindTexture(GL_TEXTURE_2D, texture_ID);
 
		// Set texture clamping method
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
 
		// Set texture interpolation method to use linear interpolation (no MIPMAPS)
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 
		// Specify the texture specification
		glTexImage2D(GL_TEXTURE_2D, 				// Type of texture
					 0,								// Pyramid level (for mip-mapping) - 0 is the top level
					 ilGetInteger(IL_IMAGE_BPP),	// Image colour depth
					 ilGetInteger(IL_IMAGE_WIDTH),	// Image width
					 ilGetInteger(IL_IMAGE_HEIGHT),	// Image height
					 0,								// Border width in pixels (can either be 1 or 0)
					 ilGetInteger(IL_IMAGE_FORMAT),	// Image format (i.e. RGB, RGBA, BGR etc.)
					 GL_UNSIGNED_BYTE,				// Image data type
					 ilGetData());					// The actual image data itself
		
		// Saving the texture
		sheet.texture_ID = texture_ID;
		sheet.width = ilGetInteger(IL_IMAGE_WIDTH);
		sheet.height = ilGetInteger(IL_IMAGE_HEIGHT);	
	}
	else {
		// If we fail to open the image
		error = ilGetError();
		exit(-1);
	}

	ilDeleteImages(1, &image_ID);
	
	/*ilEnable(IL_ORIGIN_SET);
	ilOriginFunc(IL_ORIGIN_LOWER_LEFT);

	ilEnable(IL_TYPE_SET);
	ilTypeFunc(IL_UNSIGNED_BYTE);

	ilEnable(IL_FORMAT_SET);
	ilFormatFunc(IL_RGB);*/
	
	//sheet.texture_ID = ilutGLLoadImage((char*)file_name);
	//sheet.width = ilGetInteger(IL_IMAGE_WIDTH);
	//sheet.height = ilGetInteger(IL_IMAGE_HEIGHT);
}

void Texture2D::SetFrameSize(int width, int height)
{
	size.width = width;
	size.height = height;
	size.normalized_width = (float)width/(float)sheet.width;
	size.normalized_height = (float)height/(float)sheet.height;
}

void Texture2D::SetNumberOfAnimations(int number)
{
	number_of_animations = number;
	animations.reserve(number);
	for(int i=0 ; i<number ; i++)
	{
		Animation *anim = new Animation();
		animations.push_back(anim);
	}
}

void Texture2D::AddAnimationFrame(int animation_number, int x, int y)
{
	float u,v;
	u = (float)x/sheet.width;
	v = (float)y/sheet.height;

	XY_Coordinate *xy = new XY_Coordinate((float)x,(float)y, u,v);
	animations[animation_number]->coordinates.push_back(xy);
}

void Texture2D::AddAnimationRow(int animation_number, int start_X, int start_Y, int spacing_X, int spacing_Y, int num_frames)
{
	int x,y;
	x = start_X;
	y = start_Y;
	for(int i=0 ; i<num_frames ; i++)
	{
		x += spacing_X;
		y += spacing_Y;
		AddAnimationFrame(animation_number, x, y);
	}
}

/* Convenience Functions */
void Texture2D::SetAllAnimationSpeeds(int seconds) {
	for (int i=0; i<(int)animations.size(); i++) {
		animations[i]->frame_delay = seconds * CLOCKS_PER_SEC;
	}
}

void Texture2D::SetAllAnimationSpeedsMS(int milliseconds) {
	for (int i=0; i<(int)animations.size(); i++) {
		animations[i]->frame_delay = milliseconds * CLOCKS_PER_SEC / 1000;
	}
}

void Texture2D::SetCurrentAnimation(int anim) {
	current_animation = anim;
	if(current_animation >= (int)animations.size()) 
		current_animation = 0;
}

void Texture2D::NextFrame() {
	animations[current_animation]->NextFrame();
}

/* Draw Functions */
void Texture2D::Draw() {
	
}

void Texture2D::Draw(VectorXYZ position, VectorXYZ orientation, VectorXYZ up, float scale,  float width, float height) {
	if (width == 0)
		width = (float)sheet.width;
	if (height == 0)
		height = (float)sheet.height;
	
	/* draw the sprite */
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	glPushMatrix();

	/* get the texture coordinate from the sprite so we know which frame to draw */
	Animation *anim = animations[current_animation];

	int current_frame = anim->current_frame;
	float u,v;
	/*
	u = anim->coordinates[current_frame]->u;
	v = anim->coordinates[current_frame]->v;*/
	u = v = 0;

	// this is the origin point (the position of the button)
	VectorXYZ point_TL; // Top Left 
	
	VectorXYZ point_BL; // Bottom Left
	VectorXYZ point_BR; // Bottom Right
	VectorXYZ point_TR; // Top Right

	point_TL += position;
	point_BL = point_TL - ((height*scale) * up);
	point_TR = point_TL + ((width*scale) * (orientation % up));
	point_BR = point_TR + point_BL - point_TL;

	glColor3f(1,1,1);
	// bind the appropriate texture frame
	glBindTexture(GL_TEXTURE_2D, sheet.texture_ID);

	// draw the image as a quad the size of the first loaded image
	glEnable(GL_TEXTURE_2D);
	glBegin(GL_QUADS);
		glTexCoord2f(u, v);
		glVertex3f	(point_TL.x, point_TL.y, point_TL.z); // Top Left

		glTexCoord2f(u, v+size.normalized_height);
		glVertex3f	(point_BL.x, point_BL.y, point_BL.z); // Bottom Left

		glTexCoord2f(u+size.normalized_width, v+size.normalized_height); 
		glVertex3f	(point_BR.x, point_BR.y, point_BR.z); // Bottom Right

		glTexCoord2f(u+size.normalized_width, v);
		glVertex3f	(point_TR.x, point_TR.y, point_TR.z); // Top Right
	glEnd();
	glPopMatrix();
	glDisable(GL_BLEND);
	glDisable(GL_TEXTURE_2D);
}
